package cn.com.anysdk.redis.impl;

import cn.com.anysdk.redis.api.IRedisService;
import cn.com.anysdk.redis.config.RedisConfigProperties;
import cn.com.anysdk.redis.event.RedisEventManager;
import cn.com.anysdk.redis.exception.RedisCommandException;
import cn.com.anysdk.redis.exception.RedisConnectionException;
import cn.com.anysdk.redis.exception.RedisException;
import cn.com.anysdk.redis.exception.RedisExceptionHandler;
import cn.com.anysdk.redis.factory.RedisConnectionFactory;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.*;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.util.*;
import java.util.function.Function;

/**
 * Redis服务抽象基类
 * 包含所有通用的Redis操作实现
 */
@Slf4j
public abstract class AbstractRedisServiceImpl implements IRedisService {

    protected RedisConfigProperties redisProperties;
    protected RedisEventManager eventManager;
    protected RedisConnectionFactory connectionFactory;
    protected RedisExceptionHandler exceptionHandler;
    protected Jedis jedis;
    protected Pipeline pipeline;
    protected Transaction transaction;

    /**
     * 构造函数
     */
    public AbstractRedisServiceImpl(RedisConfigProperties redisProperties, RedisEventManager eventManager) {
        this.redisProperties = redisProperties;
        this.eventManager = eventManager;
        init();
    }

    /**
     * 初始化Redis服务
     */
    @PostConstruct
    public void init() {
        this.exceptionHandler = new RedisExceptionHandler(eventManager);
        this.connectionFactory = new RedisConnectionFactory(redisProperties, eventManager);
        onInit();
    }

    /**
     * 子类可重写的初始化方法
     */
    protected abstract void onInit();

    /**
     * 获取服务名称（用于日志）
     */
    protected abstract String getServiceName();

    /**
     * 获取Jedis连接
     */
    protected Jedis getJedis() {
        if (jedis == null) {
            jedis = connectionFactory.getConnection();
        }
        return jedis;
    }

    /**
     * 执行Redis命令并处理异常
     */
    protected <T> T executeCommand(Function<Jedis, T> operation) {
        try {
            return operation.apply(getJedis());
        } catch (Exception e) {
            try {
                exceptionHandler.handleException(e, this, redisProperties.getEnvironment());
            } catch (RedisException ex) {
                log.error("{}Redis命令执行异常", getServiceName(), ex);
                if (ex instanceof RedisConnectionException) {
                    jedis = null;
                }
            }
            throw new RedisCommandException.ExecutionException(getServiceName() + "Redis命令执行失败: " + e.getMessage(), e);
        }
    }

    // String 操作
    @Override
    public boolean set(String key, String value) {
        return executeCommand(jedis -> {
            jedis.set(key, value);
            return true;
        });
    }

    @Override
    public boolean setEx(String key, String value, long expireSeconds) {
        return executeCommand(jedis -> {
            jedis.setex(key, expireSeconds, value);
            return true;
        });
    }

    @Override
    public String get(String key) {
        return executeCommand(jedis -> jedis.get(key));
    }

    @Override
    public boolean del(String key) {
        return executeCommand(jedis -> jedis.del(key) > 0);
    }

    @Override
    public boolean exists(String key) {
        return executeCommand(jedis -> jedis.exists(key));
    }

    @Override
    public long expire(String key, long seconds) {
        return executeCommand(jedis -> jedis.expire(key, seconds));
    }

    @Override
    public long ttl(String key) {
        return executeCommand(jedis -> jedis.ttl(key));
    }

    // Hash 操作
    @Override
    public boolean hSet(String key, String field, String value) {
        return executeCommand(jedis -> jedis.hset(key, field, value) > 0);
    }

    @Override
    public String hGet(String key, String field) {
        return executeCommand(jedis -> jedis.hget(key, field));
    }

    @Override
    public Map<String, String> hGetAll(String key) {
        return executeCommand(jedis -> jedis.hgetAll(key));
    }

    @Override
    public long hDel(String key, String... fields) {
        return executeCommand(jedis -> jedis.hdel(key, fields));
    }

    @Override
    public boolean hExists(String key, String field) {
        return executeCommand(jedis -> jedis.hexists(key, field));
    }

    @Override
    public long hLen(String key) {
        return executeCommand(jedis -> jedis.hlen(key));
    }

    @Override
    public Set<String> hKeys(String key) {
        return executeCommand(jedis -> jedis.hkeys(key));
    }

    @Override
    public List<String> hVals(String key) {
        return executeCommand(jedis -> jedis.hvals(key));
    }

    // List 操作
    @Override
    public long lPush(String key, String... values) {
        return executeCommand(jedis -> jedis.lpush(key, values));
    }

    @Override
    public long rPush(String key, String... values) {
        return executeCommand(jedis -> jedis.rpush(key, values));
    }

    @Override
    public String lPop(String key) {
        return executeCommand(jedis -> jedis.lpop(key));
    }

    @Override
    public String rPop(String key) {
        return executeCommand(jedis -> jedis.rpop(key));
    }

    @Override
    public long lLen(String key) {
        return executeCommand(jedis -> jedis.llen(key));
    }

    @Override
    public List<String> lRange(String key, long start, long end) {
        return executeCommand(jedis -> jedis.lrange(key, start, end));
    }

    @Override
    public String lIndex(String key, long index) {
        return executeCommand(jedis -> jedis.lindex(key, index));
    }

    @Override
    public boolean lSet(String key, long index, String value) {
        return executeCommand(jedis -> {
            jedis.lset(key, index, value);
            return true;
        });
    }

    @Override
    public long lRem(String key, long count, String value) {
        return executeCommand(jedis -> jedis.lrem(key, count, value));
    }

    // Set 操作
    @Override
    public long sAdd(String key, String... members) {
        return executeCommand(jedis -> jedis.sadd(key, members));
    }

    @Override
    public long sRem(String key, String... members) {
        return executeCommand(jedis -> jedis.srem(key, members));
    }

    @Override
    public boolean sIsMember(String key, String member) {
        return executeCommand(jedis -> jedis.sismember(key, member));
    }

    @Override
    public Set<String> sMembers(String key) {
        return executeCommand(jedis -> jedis.smembers(key));
    }

    @Override
    public long sCard(String key) {
        return executeCommand(jedis -> jedis.scard(key));
    }

    @Override
    public Set<String> sInter(String... keys) {
        return executeCommand(jedis -> jedis.sinter(keys));
    }

    @Override
    public Set<String> sUnion(String... keys) {
        return executeCommand(jedis -> jedis.sunion(keys));
    }

    @Override
    public Set<String> sDiff(String... keys) {
        return executeCommand(jedis -> jedis.sdiff(keys));
    }

    // ZSet 操作
    @Override
    public long zAdd(String key, double score, String member) {
        return executeCommand(jedis -> jedis.zadd(key, score, member));
    }

    @Override
    public long zRem(String key, String... members) {
        return executeCommand(jedis -> jedis.zrem(key, members));
    }

    @Override
    public double zScore(String key, String member) {
        return executeCommand(jedis -> {
            Double score = jedis.zscore(key, member);
            return score != null ? score : 0.0;
        });
    }

    @Override
    public long zCard(String key) {
        return executeCommand(jedis -> jedis.zcard(key));
    }

    @Override
    public Set<String> zRange(String key, long start, long end) {
        return executeCommand(jedis -> new HashSet<>(jedis.zrange(key, start, end)));
    }

    @Override
    public Set<String> zRevRange(String key, long start, long end) {
        return executeCommand(jedis -> new HashSet<>(jedis.zrevrange(key, start, end)));
    }

    @Override
    public Set<String> zRangeByScore(String key, double min, double max) {
        return executeCommand(jedis -> new HashSet<>(jedis.zrangeByScore(key, min, max)));
    }

    @Override
    public Set<String> zRevRangeByScore(String key, double max, double min) {
        return executeCommand(jedis -> new HashSet<>(jedis.zrevrangeByScore(key, max, min)));
    }

    // 事务操作
    @Override
    public void multi() {
        try {
            transaction = getJedis().multi();
        } catch (Exception e) {
            log.error("开启{}事务失败", getServiceName(), e);
            throw new RedisCommandException.ExecutionException("开启事务失败: " + e.getMessage(), e);
        }
    }

    @Override
    public void exec() {
        if (transaction == null) {
            throw new RedisCommandException("事务未开启，无法执行");
        }
        try {
            transaction.exec();
            transaction = null;
        } catch (Exception e) {
            log.error("执行{}事务失败", getServiceName(), e);
            transaction = null;
            throw new RedisCommandException.ExecutionException("执行事务失败: " + e.getMessage(), e);
        }
    }

    @Override
    public void discard() {
        if (transaction == null) {
            throw new RedisCommandException("事务未开启，无法取消");
        }
        try {
            transaction.discard();
            transaction = null;
        } catch (Exception e) {
            log.error("取消{}事务失败", getServiceName(), e);
            transaction = null;
            throw new RedisCommandException.ExecutionException("取消事务失败: " + e.getMessage(), e);
        }
    }

    // 管道操作
    @Override
    public void pipeline() {
        try {
            pipeline = getJedis().pipelined();
        } catch (Exception e) {
            log.error("开启{}管道失败", getServiceName(), e);
            throw new RedisCommandException.ExecutionException("开启管道失败: " + e.getMessage(), e);
        }
    }

    @Override
    public void sync() {
        if (pipeline == null) {
            throw new RedisCommandException("管道未开启，无法同步");
        }
        try {
            pipeline.sync();
            pipeline = null;
        } catch (Exception e) {
            log.error("同步{}管道失败", getServiceName(), e);
            pipeline = null;
            throw new RedisCommandException.ExecutionException("同步管道失败: " + e.getMessage(), e);
        }
    }

    // Lua脚本
    @Override
    public Object eval(String script, List<String> keys, List<String> args) {
        return executeCommand(jedis -> jedis.eval(script, keys, args));
    }

    // 连接管理
    @Override
    public boolean ping() {
        try {
            return "PONG".equals(getJedis().ping());
        } catch (Exception e) {
            log.error("{}Redis ping失败", getServiceName(), e);
            jedis = null;
            return false;
        }
    }

    @Override
    public void close() {
        if (jedis != null) {
            try {
                jedis.close();
            } catch (Exception e) {
                log.error("关闭{}Redis连接失败", getServiceName(), e);
            } finally {
                jedis = null;
            }
        }
        if (pipeline != null) {
            try {
                pipeline.close();
            } catch (Exception e) {
                log.error("关闭{}Redis管道失败", getServiceName(), e);
            } finally {
                pipeline = null;
            }
        }
        transaction = null;
    }

    // 键管理
    @Override
    public Set<String> keys(String pattern) {
        return executeCommand(jedis -> jedis.keys(pattern));
    }

    @Override
    public String type(String key) {
        return executeCommand(jedis -> jedis.type(key));
    }

    @Override
    public long memoryUsage(String key) {
        return executeCommand(jedis -> {
            Long usage = jedis.memoryUsage(key);
            return usage != null ? usage : 0L;
        });
    }

    // ===== 数据库管理操作 =====

    @Override
    public boolean flushAll() {
        return executeCommand(jedis -> {
            String result = jedis.flushAll();
            return "OK".equals(result);
        });
    }

    @Override
    public boolean flushDB() {
        return executeCommand(jedis -> {
            String result = jedis.flushDB();
            return "OK".equals(result);
        });
    }

    @PreDestroy
    public void destroy() {
        close();
        if (connectionFactory != null) {
            connectionFactory.close();
        }
    }
}